package ru.myrtle.umf.impl;

import java.util.*;
import ru.myrtle.umf.*;
import ru.myrtle.umf.util.*;

public class UClassOperations extends UClassOperationsStub  {

    private static class Chooser implements UPredicate<UFeature> {

        private final UClass type;

        private Chooser(UClass type) {
            this.type = type;
        }

        @Override
        public boolean evaluate(UFeature object) {
            return object.uClass() == type;
        }
    }

    /*
     *
     */

    private static class Caster<Type extends UFeature> implements UTransformer<UFeature, Type> {

        private final Class<Type> type;

        private Caster(Class<Type> type) {
            this.type = type;
        }

        @Override
        public Type transform(UFeature source) {
            return type.cast(source);
        }
    }

    /*
     *
     */

    private static class IdentifyingAttributeChooser implements UPredicate<UAttribute> {

        @Override
        public boolean evaluate(UAttribute object) {
            return object.isIdentifyingAttribute();
        }
    }

    /*
     *
     */

    private static final Chooser CHOOSER_ATTRIBUTE = new Chooser(UModel.UATTRIBUTE);
    private static final Chooser CHOOSER_REFERENCE = new Chooser(UModel.UREFERENCE);

    private static final IdentifyingAttributeChooser CHOOSER_IDENTIFYING_ATTRIBUTE = new IdentifyingAttributeChooser();

    private static final Caster<UAttribute> CASTER_ATTRIBUTE = new Caster<UAttribute>(UAttribute.class);
    private static final Caster<UReference> CASTER_REFERENCE = new Caster<UReference>(UReference.class);

    /*
     *
     */

    private <Type extends UFeature> UList<Type> select(UList<UFeature> features, Chooser chooser, Caster<Type> caster) {
        UList<Type> selected = new UObjectList<Type>();

        UUtilities.select(
                selected, features, chooser, caster
        );

        return UUtilities.unmodifiableUList(selected);
    }

    /*
     *
     */

    @Override
    public UList<UAttribute> getIdentifyingAttributes(UClass self) {
        UList<UAttribute> identifyingAttributes = new UObjectList<UAttribute>();

        UUtilities.select(
                self.getAllAttributes(), identifyingAttributes, CHOOSER_IDENTIFYING_ATTRIBUTE
        );

        return UUtilities.unmodifiableUList(identifyingAttributes);
    }

    @Override
    public boolean isSetIdentifyingAttributes(UClass self) {
        return true;
    }

    @Override
    public UList<UClass> getAllSuperTypes(UClass self) {
        UList<UClass> superTypes = new UObjectList<UClass>();

        superTypes.addAll(
                self.getSuperTypes()
        );

        for (UClass superType : self.getSuperTypes()) {
            superTypes.addAll(
                    superType.getAllSuperTypes()
            );
        }

        return UUtilities.unmodifiableUList(superTypes);
    }

    @Override
    public boolean isSetAllSuperTypes(UClass self) {
        return true;
    }

    @Override
    public UList<UFeature> getAllFeatures(UClass self) {
        UList<UFeature> features = new UObjectList<UFeature>(
                self.getFeatures()
        );

        for (UClass ancestor : self.getSuperTypes()) {
            features.addAll(
                    ancestor.getAllFeatures()
            );
        }

        return features;
    }

    @Override
    public boolean isSetAllFeatures(UClass self) {
        return true;
    }

    @Override
    public UList<UAttribute> getAttributes(UClass self) {
        return select(
                self.getFeatures(), CHOOSER_ATTRIBUTE, CASTER_ATTRIBUTE
        );
    }

    @Override
    public boolean isSetAttributes(UClass self) {
        return true;
    }

    @Override
    public UList<UAttribute> getAllAttributes(UClass self) {
        return select(
                self.getAllFeatures(), CHOOSER_ATTRIBUTE, CASTER_ATTRIBUTE
        );
    }

    @Override
    public boolean isSetAllAttributes(UClass self) {
        return true;
    }

    @Override
    public UList<UReference> getReferences(UClass self) {
        return select(
                self.getFeatures(), CHOOSER_REFERENCE, CASTER_REFERENCE
        );
    }

    @Override
    public boolean isSetReferences(UClass self) {
        return true;
    }

    @Override
    public UList<UReference> getAllReferences(UClass self) {
        return select(
                self.getAllFeatures(), CHOOSER_REFERENCE, CASTER_REFERENCE
        );
    }

    @Override
    public boolean isSetAllReferences(UClass self) {
        return true;
    }

    @Override
    public UList<UOperation> getAllOperations(UClass self) {
        UList<UOperation> operations = new UObjectList<UOperation>(
                self.getOperations()
        );

        for (UClass ancestor : self.getSuperTypes()) {
            operations.addAll(
                    ancestor.getAllOperations()
            );
        }

        return operations;
    }

    @Override
    public boolean isSetAllOperations(UClass self) {
        return true;
    }

    @Override
    public boolean isDirectSuperType(UClass self, UClass someClass) {
        return someClass.getInstanceClass().getSuperclass() == self.getInstanceClass();
    }

    @Override
    public boolean isSuperType(UClass self, UClass someClass) {
        return someClass.getInstanceClass().isAssignableFrom(
                self.getInstanceClass()
        );
    }

    @Override
    public UObject createInstance(UClass self) {
        return self.getPackage().getFactory().createInstance(self);
    }

}
